/*
 * z64
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
**/

#include "rsp.h"
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>

#define GENDEBUG

typedef int (* gen_f)(RSP_REGS & rsp);

struct gen_t {
  UINT32 crc;
  void * lib;
  gen_f f;
#ifdef GENDEBUG
  char name[32];
#endif
};

struct opinfo_t {
  int visit, labeled;
  int label;

  int nbgen;
  int szgen;
  gen_t * gentable;
  gen_t * curgen;
};

struct branch_t {
  int start, end;
};

static int curvisit;
static opinfo_t opinfo[0x1000/4];
static int nb_branches;
static branch_t branches[256];
static int nb_labels;
static int labels[256];

#define OPI(pc) opinfo[(pc)>>2]
/*inline*/ void SETLABEL(int pc) {
  //printf("%x\n", pc);
  //pc &= 0xfff;
  assert(pc >= 0 && pc < 0x1000);
  if (OPI(pc).labeled != curvisit) {
    labels[nb_labels] = pc;
    OPI(pc).label = nb_labels++;
    assert(nb_labels < sizeof(labels)/sizeof(labels[0]));
    OPI(pc).labeled = curvisit;
  }
}

#define ABS(addr) (((addr) << 2) & 0xfff)
#define REL(offset) ((pc + ((offset) << 2)) & 0xfff)

static UINT32 prep_gen(int pc, UINT32 crc, int & len)
{
	UINT32 op;
  int br = 0;

  branches[nb_branches].start = pc;

	while ( !br )
	{
    if (OPI(pc).visit == curvisit) {
      SETLABEL((pc)&0xfff);
      SETLABEL((pc+4)&0xfff);
      break;
    }

    OPI(pc).visit = curvisit;
    
		op = ROPCODE(pc);
    crc = ((crc<<1)|(crc>>31))^op^pc;
    pc = (pc+4)&0xfff;
    len++;

		switch (op >> 26)
		{
			case 0x00:	/* SPECIAL */
			{
				switch (op & 0x3f)
				{
					case 0x08:	/* JR */
            br = 1;
            break;
					case 0x09:	/* JALR */
            //br = 1;
            break;
					case 0x0d:	/* BREAK */
            br = 1;
						break;
				}
				break;
			}

			case 0x01:	/* REGIMM */
			{
				switch (RTREG)
				{
					case 0x00:	/* BLTZ */
					case 0x01:	/* BGEZ */
            SETLABEL(REL(SIMM16));
            break;
					case 0x11:	/* BGEZAL */
            //br = 1;
            break;
				}
				break;
			}

			case 0x02:	/* J */
        SETLABEL(ABS(UIMM26));
        br = 1;
        break;
			case 0x04:	/* BEQ */
			case 0x05:	/* BNE */
			case 0x06:	/* BLEZ */
			case 0x07:	/* BGTZ */
        SETLABEL(REL(SIMM16));
        break;
			case 0x03:	/* JAL */
        //SETLABEL(ABS(UIMM26));
        //br = 1;
        break;
		}

	}

  branches[nb_branches++].end = pc;
  assert(nb_branches < sizeof(branches)/sizeof(branches[0]));

  return crc;
}

static char tmps[1024];
static char * delayed;
static int has_cond;

#define COND                                    \
  has_cond = 1, fprintf

#define NOCOND() \
  if (cont && OPI((pc+4)&0xfff).labeled == curvisit) { \
    COND(fp, "cond = 1; \n");                          \
  } else                                               \
    has_cond = 0


static void D_JUMP_ABS(UINT32 addr)
{
  int a = addr&0xfff;
  sprintf(tmps, "%s { /*if (rsp.inval_gen) { rsp.nextpc=0x%x; return 0; }*/ %s goto L%d; }", has_cond? "if (cond)":"", a, has_cond? "cond=0; ":"", OPI(a).label);
  delayed = tmps;
}

static void D_JUMP_REL(int pc, int offset)
{
  D_JUMP_ABS(pc+4 + ((offset) << 2));
}

static void D_JUMP()
{
  sprintf(tmps, "%s { return 0; }", has_cond? "if (cond)":"");
  delayed = tmps;
}

static void D_JUMPL(int pc)
{
  sprintf(tmps,
          "%s { \n"
          "%s"
          "  int res;\n"
          "  if (res = rsp_jump(rsp.nextpc)) return res; \n"
          "  if (/*rsp.inval_gen || */sp_pc != 0x%x) return 0; \n"
          "}", has_cond? "if (cond)":"", has_cond?"  cond=0; \n":"", (pc+8)&0xfff);
  delayed = tmps;
  has_cond = 1;
}

static void dogen(const char * s, UINT32 op, FILE * fp)
{
  fprintf(fp, "#define op 0x%x\n%s\n#undef op\n", op, s);
}

#define GEN(s) dogen(s, op, fp)

static void rsp_gen(int pc)
{
  int i;
  const char * old_delayed;
  int oldbr, br;
  
  curvisit++;
  if (!curvisit) {
    // we looped, reset all visit counters
    for (i=0; i<0x1000/4; i++) {
      opinfo[i].visit = 0;
      opinfo[i].labeled = 0;
    }
    curvisit++;
  }

  nb_branches = 0;
  nb_labels = 0;

  int len = 0;
  UINT32 crc = prep_gen(pc, 0, len);

  for (i=0; i<nb_labels; i++) {
    if (OPI(labels[i]).visit != curvisit)
      crc = prep_gen(labels[i], crc, len);
  }

  char src[128];
  char lib[128];
  char sym[128];
  sprintf(lib, "z64/rspgen/%x-%x-%x.so", crc, pc, len);
  sprintf(sym, "doit%x", crc);

  opinfo_t * opi = &OPI(pc);
  if (opi->gentable) {
    for (i=0; i<opi->nbgen; i++)
      if (opi->gentable[i].crc == crc) {
        opi->curgen = opi->gentable + i;
        return;
      }
  }
  if (opi->nbgen >= opi->szgen) {
    if (opi->szgen)
      opi->szgen *= 2;
    else
      opi->szgen = 4;
    opi->gentable = (gen_t *) realloc(opi->gentable, sizeof(gen_t)*(opi->szgen));
  }
  gen_t * gen;
  gen = opi->gentable + opi->nbgen++;
  opi->curgen = gen;

#ifdef GENDEBUG
  strcpy(gen->name, lib);
#endif

  gen->crc = crc;
  gen->lib = dlopen(lib, RTLD_NOW);
  if (gen->lib) {
    gen->f = (gen_f) dlsym(gen->lib, sym);
    assert(gen->f);
    fprintf(stderr, "reloaded %s\n", lib);
    return;
  }
//   else
//     printf("%s\n", dlerror()); 
  
  sprintf(src, "z64/rspgen/%x-%x-%x.cpp", crc, pc, len);
  FILE * fp = fopen(src, "w");

  fprintf(fp,
          "#include \"rsp.h\"\n"
          "\n"
          "extern \"C\" {\n"
          "int %s() {\n"
          "int cond=0;\n",
          sym);

  for (i=0; i<nb_branches; i++) {
    int cont = 1;
    delayed = 0;
    //fprintf(stderr, "branch %x --> %x\n", branches[i].start, branches[i].end-4);
    for (pc=branches[i].start; cont || delayed; pc = (pc+4)&0xfff) {
      UINT32 op = ROPCODE(pc);
      char s[128];
      rsp_dasm_one(s, pc, op);
      if (cont && OPI(pc).labeled == curvisit)
        fprintf(fp, "L%d: ;\n", OPI(pc).label);
      //fprintf(fp, "/* %3x\t%s */\n", pc, s);
      fprintf(fp, "GENTRACE(\"%3x\t%s\\n\");\n", pc, s);
      oldbr = br;
      br = 0;
      old_delayed = delayed;
      delayed = 0;

      if (((pc+4)&0xfff)==branches[i].end)
        cont = 0;
  
      switch (op >> 26)
      {
        case 0x00:	/* SPECIAL */
        {
          switch (op & 0x3f)
          {
            case 0x08:	/* JR */
              if (!old_delayed) {
                br = 1|8|16;
                NOCOND();
                D_JUMP();
              }
              break;
            case 0x09:	/* JALR */
              if (!old_delayed) {
                br = 1;
                NOCOND();
                D_JUMPL(pc);
              }
              break;
            case 0x0d:	/* BREAK */
              br = 2|8;
              //delayed = "return 1;";
              has_cond = 0;
              break;
          }
          break;
			}
        
        case 0x01:	/* REGIMM */
        {
          switch (RTREG)
          {
            case 0x00:	/* BLTZ */
              if (!old_delayed) {
                COND(fp, "		cond=(INT32)(_RSVAL(0x%x)) < 0;\n", op);
                D_JUMP_REL(pc, _SIMM16(op));
                br = 4;
              }
              break;
            case 0x01:	/* BGEZ */
              if (!old_delayed) {
                COND(fp, "		cond=(INT32)(_RSVAL(0x%x)) >= 0;\n", op);
                D_JUMP_REL(pc, _SIMM16(op));
                br = 4;
              }
              break;
            case 0x11:	/* BGEZAL */
              br = 1;
              COND(fp, "cond=(INT32)(_RSVAL(0x%x)) >= 0;\n", op);
              D_JUMPL(pc);
              break;
          }
          break;
        }
        
        case 0x02:	/* J */
          if (!old_delayed) {
            NOCOND();
            D_JUMP_ABS(_UIMM26(op) <<2);
            br = 4|8|16;
          }
          break;
        case 0x04:	/* BEQ */
          if (!old_delayed) {
            COND(fp, "		cond=_RSVAL(0x%0x) == _RTVAL(0x%0x);\n", op, op);
            D_JUMP_REL(pc, _SIMM16(op));
            br = 4;
          }
          break;
        case 0x05:	/* BNE */
          if (!old_delayed) {
            COND(fp, "		cond=_RSVAL(0x%0x) != _RTVAL(0x%0x);\n", op, op);
            D_JUMP_REL(pc, _SIMM16(op));
            br = 4;
          }
          break;
        case 0x06:	/* BLEZ */
          if (!old_delayed) {
            COND(fp, "		cond=(INT32)_RSVAL(0x%0x) <= 0;\n", op);
            D_JUMP_REL(pc, _SIMM16(op));
            br = 4;
          }
          break;
        case 0x07:	/* BGTZ */
          if (!old_delayed) {
            COND(fp, "		cond=(INT32)_RSVAL(0x%0x) > 0;\n", op);
            D_JUMP_REL(pc, _SIMM16(op));
            br = 4;
          }
          break;
        case 0x03:	/* JAL */
          if (!old_delayed) {
            br = 1;
            NOCOND();
            D_JUMPL(pc);
          }
          break;
      }

      if (!(br&4) && (!old_delayed || !br)) {
        if (br && !(br&16)) {
          fprintf(fp, "sp_pc = 0x%x;\n", (pc + 4)&0xfff);
        }
        //fprintf(fp, "rsp_execute_one(0x%x);\n", op);





        switch (op >> 26)
        {
          case 0x00:	/* SPECIAL */
          {
            switch (op & 0x3f)
            {
              case 0x00:	/* SLL */		if (RDREG) GEN("RDVAL = (UINT32)RTVAL << SHIFT;"); break;
              case 0x02:	/* SRL */		if (RDREG) GEN("RDVAL = (UINT32)RTVAL >> SHIFT; "); break;
              case 0x03:	/* SRA */		if (RDREG) GEN("RDVAL = (INT32)RTVAL >> SHIFT; "); break;
              case 0x04:	/* SLLV */		if (RDREG) GEN("RDVAL = (UINT32)RTVAL << (RSVAL & 0x1f); "); break;
              case 0x06:	/* SRLV */		if (RDREG) GEN("RDVAL = (UINT32)RTVAL >> (RSVAL & 0x1f); "); break;
              case 0x07:	/* SRAV */		if (RDREG) GEN("RDVAL = (INT32)RTVAL >> (RSVAL & 0x1f); "); break;
              case 0x08:	/* JR */		GEN("JUMP_PC(RSVAL); "); break;
              case 0x09:	/* JALR */		GEN("JUMP_PC_L(RSVAL, RDREG); "); break;
              case 0x0d:	/* BREAK */
              {
                GEN(
                  "               \
                *z64_rspinfo.SP_STATUS_REG |= (SP_STATUS_HALT | SP_STATUS_BROKE ); \
                if ((*z64_rspinfo.SP_STATUS_REG & SP_STATUS_INTR_BREAK) != 0 ) { \
                  *z64_rspinfo.MI_INTR_REG |= 1; \
                  z64_rspinfo.CheckInterrupts(); \
                } \
                rsp.nextpc = ~0; \
                return 1; \
");
                break;
              }
              case 0x20:	/* ADD */		if (RDREG) GEN("RDVAL = (INT32)(RSVAL + RTVAL); "); break;
              case 0x21:	/* ADDU */		if (RDREG) GEN("RDVAL = (INT32)(RSVAL + RTVAL); "); break;
              case 0x22:	/* SUB */		if (RDREG) GEN("RDVAL = (INT32)(RSVAL - RTVAL); "); break;
              case 0x23:	/* SUBU */		if (RDREG) GEN("RDVAL = (INT32)(RSVAL - RTVAL); "); break;
              case 0x24:	/* AND */		if (RDREG) GEN("RDVAL = RSVAL & RTVAL; "); break;
              case 0x25:	/* OR */		if (RDREG) GEN("RDVAL = RSVAL | RTVAL; "); break;
              case 0x26:	/* XOR */		if (RDREG) GEN("RDVAL = RSVAL ^ RTVAL; "); break;
              case 0x27:	/* NOR */		if (RDREG) GEN("RDVAL = ~(RSVAL | RTVAL); "); break;
              case 0x2a:	/* SLT */		if (RDREG) GEN("RDVAL = (INT32)RSVAL < (INT32)RTVAL; "); break;
              case 0x2b:	/* SLTU */		if (RDREG) GEN("RDVAL = (UINT32)RSVAL < (UINT32)RTVAL; "); break;
              default:	GEN("unimplemented_opcode(op); "); break;
            }
            break;
          }
          
          case 0x01:	/* REGIMM */
          {
            switch (RTREG)
            {
              case 0x00:	/* BLTZ */		GEN("if ((INT32)(RSVAL) < 0) JUMP_REL(SIMM16); "); break;
              case 0x01:	/* BGEZ */		GEN("if ((INT32)(RSVAL) >= 0) JUMP_REL(SIMM16); "); break;
                // VP according to the doc, link is performed even when condition fails
              case 0x11:	/* BGEZAL */	GEN("LINK(31); if ((INT32)(RSVAL) >= 0) JUMP_REL(SIMM16); "); break;
                //case 0x11:	/* BGEZAL */	if ((INT32)(RSVAL) >= 0) JUMP_REL_L(SIMM16, 31); break;
              default:	GEN("unimplemented_opcode(op); "); break;
            }
            break;
          }
          
          case 0x02:	/* J */			GEN("JUMP_ABS(UIMM26); "); break;
          case 0x03:	/* JAL */		GEN("JUMP_ABS_L(UIMM26, 31); "); break;
          case 0x04:	/* BEQ */		GEN("if (RSVAL == RTVAL) JUMP_REL(SIMM16); "); break;
          case 0x05:	/* BNE */		GEN("if (RSVAL != RTVAL) JUMP_REL(SIMM16); "); break;
          case 0x06:	/* BLEZ */	GEN("if ((INT32)RSVAL <= 0) JUMP_REL(SIMM16); "); break;
          case 0x07:	/* BGTZ */	GEN("if ((INT32)RSVAL > 0) JUMP_REL(SIMM16); "); break;
          case 0x08:	/* ADDI */	if (RTREG) GEN("RTVAL = (INT32)(RSVAL + SIMM16); "); break;
          case 0x09:	/* ADDIU */	if (RTREG) GEN("RTVAL = (INT32)(RSVAL + SIMM16); "); break;
          case 0x0a:	/* SLTI */	if (RTREG) GEN("RTVAL = (INT32)(RSVAL) < ((INT32)SIMM16); "); break;
          case 0x0b:	/* SLTIU */	if (RTREG) GEN("RTVAL = (UINT32)(RSVAL) < (UINT32)((INT32)SIMM16); "); break;
          case 0x0c:	/* ANDI */	if (RTREG) GEN("RTVAL = RSVAL & UIMM16; "); break;
          case 0x0d:	/* ORI */		if (RTREG) GEN("RTVAL = RSVAL | UIMM16; "); break;
          case 0x0e:	/* XORI */	if (RTREG) GEN("RTVAL = RSVAL ^ UIMM16; "); break;
          case 0x0f:	/* LUI */		if (RTREG) GEN("RTVAL = UIMM16 << 16; "); break;
            
          case 0x10:	/* COP0 */
          {
            switch ((op >> 21) & 0x1f)
            {
              case 0x00:	/* MFC0 */		if (RTREG) GEN("RTVAL = get_cop0_reg(rsp, RDREG); "); break;
              case 0x04:	/* MTC0 */
              {
                GEN("set_cop0_reg(rsp, RDREG, RTVAL); \n");
                if (RDREG == 0x08/4) {
                  fprintf(fp,
                          "if (rsp.inval_gen) {\n"
                          "   rsp.inval_gen = 0;\n"
                          "   sp_pc = 0x%x; \n"
                          "   return 2; \n"
                          "}\n"
                          , (pc + 4)&0xfff);
                }
                break;
              }
              default:
                printf("unimplemented cop0 %x (%x)\n", (op >> 21) & 0x1f, op);
                break;
            }
            break;
          }
          
          case 0x12:	/* COP2 */
          {
            switch ((op >> 21) & 0x1f)
            {
              case 0x00:	/* MFC2 */
              {
                // 31       25      20      15      10     6         0
                // ---------------------------------------------------
                // | 010010 | 00000 | TTTTT | DDDDD | IIII | 0000000 |
                // ---------------------------------------------------
                //
                if (RTREG) GEN("\
                {int el = (op >> 7) & 0xf;\
                UINT16 b1 = VREG_B(VS1REG, (el+0) & 0xf);\
                UINT16 b2 = VREG_B(VS1REG, (el+1) & 0xf);\
                RTVAL = (INT32)(INT16)((b1 << 8) | (b2));}\
");
                break;
              }
              case 0x02:	/* CFC2 */
              {
                // 31       25      20      15      10            0
                // ------------------------------------------------
                // | 010010 | 00010 | TTTTT | DDDDD | 00000000000 |
                // ------------------------------------------------
                //
                
                // VP to sign extend or to not sign extend ?
                //if (RTREG) RTVAL = (INT16)rsp.flag[RDREG];
                if (RTREG) GEN("RTVAL = rsp.flag[RDREG];");
                break;
              }
              case 0x04:	/* MTC2 */
              {
                // 31       25      20      15      10     6         0
                // ---------------------------------------------------
                // | 010010 | 00100 | TTTTT | DDDDD | IIII | 0000000 |
                // ---------------------------------------------------
                //
                GEN("\
                {int el = (op >> 7) & 0xf;\
                VREG_B(VS1REG, (el+0) & 0xf) = (RTVAL >> 8) & 0xff;\
                VREG_B(VS1REG, (el+1) & 0xf) = (RTVAL >> 0) & 0xff;}\
");
                break;
              }
              case 0x06:	/* CTC2 */
              {
                // 31       25      20      15      10            0
                // ------------------------------------------------
                // | 010010 | 00110 | TTTTT | DDDDD | 00000000000 |
                // ------------------------------------------------
                //
                
                GEN("rsp.flag[RDREG] = RTVAL & 0xffff;");
                break;
              }
              
              case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
              case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
              {
                GEN("handle_vector_ops(rsp, op);");
                break;
              }
              
              default:	GEN("unimplemented_opcode(op); "); break;
            }
            break;
          }
          
        case 0x20:	/* LB */		if (RTREG) GEN("RTVAL = (INT32)(INT8)READ8(RSVAL + SIMM16); "); break;
        case 0x21:	/* LH */		if (RTREG) GEN("RTVAL = (INT32)(INT16)READ16(RSVAL + SIMM16); "); break;
        case 0x23:	/* LW */		if (RTREG) GEN("RTVAL = READ32(RSVAL + SIMM16); "); break;
        case 0x24:	/* LBU */		if (RTREG) GEN("RTVAL = (UINT8)READ8(RSVAL + SIMM16); "); break;
        case 0x25:	/* LHU */		if (RTREG) GEN("RTVAL = (UINT16)READ16(RSVAL + SIMM16); "); break;
        case 0x28:	/* SB */		GEN("WRITE8(RSVAL + SIMM16, RTVAL); "); break;
        case 0x29:	/* SH */		GEN("WRITE16(RSVAL + SIMM16, RTVAL); "); break;
        case 0x2b:	/* SW */		GEN("WRITE32(RSVAL + SIMM16, RTVAL); "); break;
        case 0x32:	/* LWC2 */		GEN("handle_lwc2(rsp, op); "); break;
        case 0x3a:	/* SWC2 */		GEN("handle_swc2(rsp, op); "); break;
            
          default:
            {
              GEN("unimplemented_opcode(op);");
              break;
            }
        }
        


        
//         if (br) {
//           if (br & 2)
//             fprintf(fp, "return 1;\n");
//           else
//             fprintf(fp, "return 0;\n");
//         }
      }
      if (old_delayed)
        fprintf(fp, "%s\n", old_delayed);
    }
    if (!((/*br|*/oldbr)&8) && ((!oldbr && !(br&2)) || has_cond)) {
      fprintf(fp, "/* jumping back to %x */\ngoto L%d;\n", pc, OPI(pc).label);
      assert(OPI(pc).labeled == curvisit);
    }
  }
  
  fprintf(fp, "}}\n");

  fclose(fp);

  pid_t pid = fork();
  // SDL redirect these signals, but we need them untouched for waitpid call
  signal(17, 0);
  signal(11, 0);
  if (!pid) {
//     char s[128];
//     atexit(0);
//     sprintf(s, "gcc -Iz64 -g -shared -O2 %s -o %s", src, lib);
//     system(s);
//     exit(0);

    //setsid();
    //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-g", "-O3", "-fomit-frame-pointer", src, "-o", lib, "-finline-limit=10000", 0);
    //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O3", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", 0);
    //execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O3", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", "-m3dnow", "-mmmx", "-msse", "-msse2", "-mfpmath=sse", 0);
    execl("/usr/bin/gcc", "/usr/bin/gcc", "-Iz64", "-shared", "-O6", src, "-o", lib, "-fomit-frame-pointer", "-ffast-math", "-funroll-loops", "-fforce-addr", "-finline-limit=10000", "-m3dnow", "-mmmx", "-msse", "-msse2", 0);
    printf("gnii ??\n");
    exit(0);
  }
  waitpid(pid, 0, __WALL);

  gen->lib = dlopen(lib, RTLD_NOW);
  if (!gen->lib)
    printf("%s\n", dlerror()); 
  assert(gen->lib);
  fprintf(stderr, "created and loaded %s\n", lib);
  gen->f = (gen_f) dlsym(gen->lib, sym);
  assert(gen->f);
}

void rsp_invalidate(int begin, int len)
{
  //printf("invalidate %x %x\n", begin, len);
  begin = 0; len = 0x1000;
  assert(begin+len<=0x1000);
  while (len > 0) {
    OPI(begin).curgen = 0;
    begin += 4;
    len -= 4;
  }
  rsp.inval_gen = 1;
}

int rsp_jump(int pc)
{
  pc &= 0xfff;
  sp_pc = pc;
  rsp.nextpc = ~0;
  opinfo_t * opi = &OPI(pc);
  gen_t * gen = opi->curgen;
  if (!gen) rsp_gen(pc);
  gen = opi->curgen;
  GENTRACE("rsp_jump %x (%s)\n", pc, gen->name);
  int res = gen->f(rsp);
  GENTRACE("r31 %x from %x nextpc %x pc %x res %d (%s)\n", rsp.r[31], pc, rsp.nextpc, sp_pc, res, gen->name);
  if (rsp.nextpc != ~0)
  {
    sp_pc = (rsp.nextpc & 0xfff);
    rsp.nextpc = ~0;
  }
  else
  {
    //sp_pc = ((sp_pc+4)&0xfff);
  }
  return res;
}
